package app

import (
	"bytes"
	"context"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/rs/zerolog/log"
	"github.com/sourcegraph/conc"
	"kvmc/pkg/kvm"
	"kvmc/pkg/sshx"
	"kvmc/pkg/uix"
	"strings"
	"time"
)

type StatusOption struct {
	Cluster string
	All     bool
}

func (m *Manager) GetStatus(option *StatusOption) {
	nodes := make([]*Node, 0)
	if option.All {
		nodes = m.GetAllNodes()
	} else {
		nodes = m.GetClusterNodes(option.Cluster)
	}
	infos := m.getStatusBySSH(nodes)
	if len(infos) > 1 {
		slice.SortBy(infos[1:], func(a, b []string) bool {
			return a[0] < b[0]
		})
	}
	uix.Table(infos)
}

func (m *Manager) AddToWhiteList(nodes []*Node) {
	kvmNodes := make([]*kvm.Node, 0, len(nodes))
	for _, node := range nodes {
		kvmNodes = append(kvmNodes, &kvm.Node{
			Hostname: node.Hostname,
			IP:       node.IP,
		})
	}
	kvm.NewKvm(kvmNodes)
}

func (m *Manager) getStatusByApi(nodes []*Node) [][]string {
	kvmNodes := make([]*kvm.Node, 0, len(nodes))
	for _, node := range nodes {
		kvmNodes = append(kvmNodes, &kvm.Node{
			Hostname: node.Hostname,
			IP:       node.IP,
		})
	}
	k := kvm.NewKvm(kvmNodes)
	k.RefreshStatus()
	infos := make([][]string, 0, len(k.Nodes))
	infos = append(infos, []string{"name", "hostname", "ip", "status"})
	for i, node := range k.Nodes {
		infos = append(infos, []string{nodes[i].Name, node.Hostname, node.IP, node.Status})
	}
	return infos
}

func (m *Manager) getStatusBySSH(nodes []*Node) [][]string {
	m.AddToWhiteList(nodes)
	infos := make([][]string, 0, len(nodes))
	infos = append(infos, []string{"name", "hostname", "ip", "status"})
	err := m.RefreshStatus(nodes)
	if err != nil {
		return infos
	}
	for _, node := range nodes {
		infos = append(infos, []string{node.Name, node.Hostname, node.IP, node.Status})
	}
	return infos
}

func (m *Manager) RefreshStatus(nodes []*Node) error {
	centerClient, err := m.getCenterSSHClient()
	if err != nil {
		log.Err(err).Send()
		return err
	}
	defer centerClient.Close()
	var wg conc.WaitGroup
	for _, node := range nodes {
		node := node
		wg.Go(func() {
			_, err = m.getNodeHostname(centerClient, node)
			if err != nil {
				node.Status = "shutdown"
			} else {
				node.Status = "running"
			}
		})
	}
	wg.Wait()
	return nil
}

func (m *Manager) getNodeHostname(client *sshx.SShClient, node *Node) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	cmd := `ssh root@` + node.IP + " hostname"
	dataCh := make(chan string, 3)
	stdio := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	go func() {
		err := client.Exec(cmd, stdio, stderr)
		if err != nil {
			cancel()
		}
		dataCh <- stdio.String()
	}()
	for {
		select {
		case <-ctx.Done():
			return "", ctx.Err()
		case hostname := <-dataCh:
			return strings.TrimSpace(hostname), nil
		default:
		}
	}
}
